<?
/***************
This is written by Kyle Kienapfel 2006 (doctor.whom of gmail.com)

This script reads in an address.dat file from Palm Desktop 4.1.4 or so.

Couple ideas for output: 

* Contact listing with pictures
* Birthdays in iCal format 

Google Calendar has fancy schmancy web events:
http://www.google.com/support/calendar/bin/answer.py?answer=48526
X-GOOGLE-CALENDAR-CONTENT-TITLE:Testerton's Birthday
X-GOOGLE-CALENDAR-CONTENT-ICON:http://somewhere/16x16gif or png

********/

// For future creation of a config.php
$Addrbk = "address.dat";

// Functions
if (basename($_SERVER["SCRIPT_NAME"]) == "address.php") {
function hexdebug($value) {
//echo "$value\n";
echo chunk_split(implode(" ",unpack("H*",$value)),2," ") . "\n";
}
}

if (file_exists("config.txt")) {
  include_once("config.txt");
}

function fij() {
global $value,$Category;
print "$Category ";
hexdebug($value);
}
function pmReadString() {
 global $value;
 //05 00 00 00 00 00 00 00 length string
 //length can be 00
 //referred to as "CString" online
 if (substr($value,0,8) == "\x05\0\0\0\0\0\0\0") {
   $value = substr($value,8);
   $StringLength = ord(substr($value,0,1));
   $value = substr($value,1);
   if ($StringLength != 0) {
     $LastName = substr($value,0,$StringLength);
     $value = substr($value,$StringLength);
     return $LastName;
   }
   return NULL;
 }
 else {
   
 }
}
function pmReadType() {
 global $value;
 if (substr($value,0,4) == "\x01\0\0\0") {
   $value = substr($value,4);
   list(,$Return) = unpack("V",substr($value,0,4));
   $value = substr($value,4);
   return $Return;
 }
}
// For the 36 byte chunk
function hdReadNum() {
 global $Header;
 if (substr($Header,0,4) == "\x01\0\0\0") {
   $Header = substr($Header,4);
   list(,$Return) = unpack("V",substr($Header,0,4));
   $Header = substr($Header,4);
   return $Return;
 }
}


// Start with opening the file
$AddressFile = file_get_contents($Addrbk);
$FIndex = 0;

$FIndex = 52;
$StringLength = ord(substr($AddressFile,$FIndex,1));
$FIndex += 1;
$AddressLocation = substr($AddressFile,$FIndex,$StringLength);
$FIndex += $StringLength;

// 11 nulls
$FIndex += 11;

for ($i=1;$i <= 9;$i++) {
$StringLength = ord(substr($AddressFile,$FIndex,1));
$FIndex += 1;
$CField[$i] = substr($AddressFile,$FIndex,$StringLength);
$FIndex += $StringLength;
//print names of custom fields
//print $StringLength." ".$CField[$i]; print "\n";
$FIndex += 4;
}

$FIndex += 42; // nulls
list(,$Unknown) = unpack("V",substr($AddressFile,$FIndex,4));
//echo $Unknown;
$FIndex += 4;
list(,$CategoryCount) = unpack("V",substr($AddressFile,$FIndex,4));
$FIndex += 4;


for ($i=1;$i <= $CategoryCount;$i++) {
// CatNum thats 1,2,3,4,5 , then another CatNum that goes 1,2,3,11,13, 4 nulls
$FIndex += 12;
$StringLength = ord(substr($AddressFile,$FIndex,1));
$FIndex += 1;
$CatA[$i] = substr($AddressFile,$FIndex,$StringLength);
$FIndex += $StringLength;

$StringLength = ord(substr($AddressFile,$FIndex,1));
$FIndex += 1;
$CatB[$i] = substr($AddressFile,$FIndex,$StringLength);
$FIndex += $StringLength;
//print "Cat $i: ".$CatA[$i]."\n";
}
$CatA[0] = "Unfiled";
$CatA["All"] = "All";

//print "FIndex: $FIndex\n";
//print str_replace("\0",'?',substr($AddressFile,$FIndex));
//$rest = substr($AddressFile,$FIndex);
//print $StringLength;



  /**************************************************************/
 // Determine what we're doing here

if ($argv[1] == "birthdays") {
$Birthdays=true;
} elseif ($_SERVER["PATH_INFO"] == "/birthdays.ics")  {
$Birthdays=true;
} elseif ($_SERVER["PATH_INFO"] == "/birthdays") {
$Birthdays=true;
print "<html><head><title>Birthdays in iCal format</title></head><body><pre>\n";
}
elseif (isset($_GET["PIC"])) {
// mime type?
header('Content-type: image/jpeg');
} elseif (isset($_GET["UID"])) {
  // Do nothing here, only need stuff done in loop
  /*if (file_exists("header.txt")) {
    $PageTitle="Contacts";
    include("header.txt");
  } else {
    print "<html><head><title>Address Book</title></head><body>\n";
  }*/

} else {
// Contact list
if (file_exists("header.txt")) {
  $PageTitle="Contacts";
  include("header.txt");
} else {
  print "<html><head><title>Address Book</title></head><body>\n";
}
//$CategoryCount $CatA[number]
print "<form method=GET><select name=\"CAT\">";
//for ($i=0; $i<= $CategoryCount; $i++) {
foreach ($CatA as $i => $value) {
print "<option value=\"$i\"";
// Javascipr would do this way better
if ($_GET["CAT"] == $i) { print " selected"; }
print ">".$CatA[$i]."</option>\n";
}
print "</select><input type=\"submit\" value=\"Go\">";
if (isset($_GET["CAT"])) { print " Showing ".$CatA[$_GET["CAT"]]; }
print "</form><table>\n";
}








// Entry starts 36 bytes before this delimiter
$Entries = explode("\xFF\xFF\xFF\x7F",$AddressFile);
$FileHeader=array_shift($Entries); // already processed actually
$NextHeader = substr($FileHeader,-36);
$Count=0;
//echo count($Entries)." ";
foreach ($Entries as $value) {
$Count++;
$Header=$NextHeader;
if ($Count < count($Entries)) { 
  $NextHeader = substr($value,-36); 
  $value = substr($value,0,-36); 
}
//Process 36 byte chunk with hdReadNum
//hexdebug($Header);
$HD1 = hdReadNum(); //UID 
$HD2 = hdReadNum();
$HD3 = hdReadNum(); // appeared to be 00 00 00 00
$HD3 = hdReadNum();

//print "$HD1 $HD2 $HD3\n";
list(,$Category) = unpack("V",substr($value,4,4));
$value = substr($value,8);

$value = substr($value,36);

//01 00 00 00 03 50 69 63 40 00 00 00
//hexdebug(substr($value,0,12));
unset($Image); //unset before possible set may or may not be good form
  if (substr($value,0,12) == "\x01\0\0\0\x03\x50\x69\x63\x40\0\0\0") {
    //print "image!\n";
    $value = substr($value,12);
    list(,$StringLength) = unpack("V",substr($value,0,4));
    $value = substr($value,4);
    unset($Image);
    $Image = substr($value,0,$StringLength);
    $value = substr($value,$StringLength);
  }
  else {
    //4xnull
    $value = substr($value,4);
  }
  
  $value = substr($value,26);
  $StringLength = ord(substr($value,0,1));
  $value = substr($value,1);
  if ($StringLength != 0) {
    $FirstName = substr($value,0,$StringLength);
    $value = substr($value,$StringLength);
  }
  $value = substr($value,8);
  $StringLength = ord(substr($value,0,1));
  $value = substr($value,1);
  if ($StringLength != 0) {
    $LastName = substr($value,0,$StringLength);
    $value = substr($value,$StringLength);
  }
  $value = substr($value,8);
  
  $StringLength = ord(substr($value,0,1));
  $value = substr($value,1);
  if ($StringLength != 0) {
    $Title = substr($value,0,$StringLength);
    $value = substr($value,$StringLength);
  }
  $value = substr($value,8);
  
  $StringLength = ord(substr($value,0,1));
  $value = substr($value,1);
  if ($StringLength != 0) {
    $Company = substr($value,0,$StringLength);
    $value = substr($value,$StringLength);
  }
  
  // 01 00 00 00
  $value = substr($value,4);
  //fij();continue;
  // Mostly sure this is which Phone entry to display in the list.
  list(,$DisplayField) = unpack("V",substr($value,0,4));
  $value = substr($value,4);
   
  //05 7nulls
  $value = substr($value,8);
  $StringLength = ord(substr($value,0,1));
  $value = substr($value,1);
  if ($StringLength != 0) {
    $Note = substr($value,0,$StringLength);
    $value = substr($value,$StringLength);
  }
  /*$Field1 = pmReadString();
  $Field2 = pmReadString();
  $Field3 = pmReadString();
  $Field4 = pmReadString();
  $Field5 = pmReadString();
  $Field6 = pmReadString();
  $Field7 = pmReadString();*/
  $Field[1] = pmReadString();
  $Field[2] = pmReadString();
  $Field[3] = pmReadString();
  $Field[4] = pmReadString();
  $Field[5] = pmReadString();
  $Field[6] = pmReadString();
  $Field[7] = pmReadString();


  //Field Types
  //0 Work
  //1 Home
  //2 Fax
  //Other
  //E-Mail
  //Pager
  //Mobile
  $FieldT1 = pmReadType();
  $FieldT2 = pmReadType();
  $FieldT3 = pmReadType();
  $FieldT4 = pmReadType();
  $FieldT5 = pmReadType();
  $FieldT6 = pmReadType();
  $FieldT7 = pmReadType();
  
  //$Custom = pmReadString();
  $Custom1 = pmReadString();
  $Custom2 = pmReadString();
  $Custom3 = pmReadString();
  $Custom4 = pmReadString();
  $Custom5 = pmReadString();
  $Custom6 = pmReadString();
  $Custom7 = pmReadString();
  $Custom8 = pmReadString();
  $Custom9 = pmReadString();
  //Another Type number
  //0 Work
  //1 Home
  //2 "Other"
  $AddrT1 = pmReadType();
  //Street,City,Province,Postal,Country
  $Addr = pmReadString();
  $Addr = pmReadString();
  $Addr = pmReadString();
  $Addr = pmReadString();
  $Addr = pmReadString();
  
  $AddrT2 = pmReadType();
  $Addr = pmReadString();
  $Addr = pmReadString();
  $Addr = pmReadString();
  $Addr = pmReadString();
  $Addr = pmReadString();

  $AddrT3 = pmReadType();
  $Addr = pmReadString();
  $Addr = pmReadString();
  $Addr = pmReadString();
  $Addr = pmReadString();
  $Addr = pmReadString();
  
  $IM1 = pmReadString();
  $IM2 = pmReadString();
  $IMT1 = pmReadType();
  $IMT2 = pmReadType();
  $URL = pmReadString();
  //Process Birthday
  // 4400 0000 flag
  list(,$Burp) = unpack("V",substr($value,8,4));
  $Burp = (($Burp & 0xFFFFE0) >> 5); // mask is not so important.
  $Burp2 =$Burp;
  if ($Burp != 491520) {
  //print "$Burp\n";
  // 491520-464983 = 26537   /365=72.7 years -> 2078
  // if $Burp >= 458752  step up days from 09/17/1989
  if ($Burp >= 458752) {
   $Burp = $Burp - 458752;
   $Burp = $Burp * 86400 +  gmmktime(0,0,0,9,17,1989);
   //$Burp = gmdate("M-d-Y", $Burp);
  }
  elseif ($Burp < 458752) {
   $Burp = (458752 - $Burp)/2;
   //$Burp4=$Burp;
   $Burp = gmmktime(0,0,0,9,17,1989) - $Burp * 86400;
   //$Burp = gmdate("M-d-Y", $Burp); // hisa for time
   //$Burp = "$Burp4 $Burp";
  }
  // from 308929 up to somewhere, step up by 32, from 01/01/1904
  // I don't know anyone born in the timeframe of 1904 -> probably 1930s ish
  } else { unset($Burp); }
 if (isset($FirstName) && isset($LastName)) {
    $DisplayName = "$FirstName $LastName";
 } elseif (isset($FirstName)) {
    $DisplayName = $FirstName;
 } elseif (isset($LastName)) {
    $DisplayName = $LastName;
 } else {
    $DisplayName = "??? null ???";
 }
  /*******************************************************/
 /******  Output Phase of the main loop            ******/
/*******************************************************/
  if (isset($_GET["UID"])) {
    if ($_GET["UID"] == $HD1) {
 if (file_exists("header.txt")) {
     $PageTitle="Contacts";
     $PageTitle=$DisplayName;
     include("header.txt");
   } else {
     print "<html><head><title>Address Book</title></head><body>\n";
   }
if (isset($Image)) { print "<img src=\"address.php?PIC=$HD1\">"; }
print "$DisplayName\n";
print "Mortal Kombat!";
    }
    else { continue; } 
  } elseif (isset($_GET["PIC"])) {
    if ($_GET["PIC"] == $HD1) {
      echo $Image;
    }
    else { continue; }
}
//print "fij";
  elseif ($Birthdays) {
  if (isset($Burp)) {
    //EXDATE: current years one and then have it specify age?
    print "BEGIN:VEVENT\n";
    print "SUMMARY:$FirstName $LastName's Birthday\n";
    print "DTSTART;TZID=America/Los_Angeles:".gmdate("Ymd",$Burp)."\n";
    print "DTEND;TZID=America/Los_Angeles:".gmdate("Ymd",$Burp)."\n";
    print "RRULE:FREQ=YEARLY\n";
    print "X-GOOGLE-CALENDAR-CONTENT-TITLE:$FirstName $LastName's Birthday\n";
    print "X-GOOGLE-CALENDAR-CONTENT-ICON:http://shadowmage.plinkomedia.com/gfx/birthday.gif\n";
    print "END:VEVENT\n";
  }} 
 else {
    // CAT = All
   if(isset($_GET["CAT"]) && $_GET["CAT"] != $Category && $_GET["CAT"] != "All") { continue; }
  //print "$Unknown $FirstName $LastName ";
  print "<tr><td><a href=\"address.php?UID=$HD1\">$DisplayName</a><td>";
  print $Field[$DisplayField];
  // Give a letter for Phone types? M W H F etc
  print "<td>" . $CatA[$Category];
  print "</tr>\n";
  //if (isset($Burp)) { print "Burp: ".gmdate("Ymd gisa",$Burp)." $Burp2 "; }
  //print "\n";
 } // end of else for not doing birthdays


  /**********************************************/
 /********** Back to normal looping code *******/
/**********************************************/
  unset($FirstName,$LastName);
  //fij();
  //print $Category." ";
//hexdebug($value);

} // foreach
